use std::collections::HashMap;
use std::fs::File;
use std::io::{copy, Read};

use clap::Parser;
use http_body_util::BodyExt;
use wtask_app::{App, AppRes, cli_app, cli_appres, msg_func_none};
use wtask_base::app::get_wtask_data_dir;
use wtask_base::encrypt::decrypt_str_to_str;
use wtask_base::error::{WError, WResult};
use wtask_base::export::{json, serde_from_str, serde_to_string};
use wtask_file::{BufBody, IncomingBody, Request, Response, StatusCode, header};

use crate::basic::{parse_reader, response_body, response_file_stream, ResponseBody, WEB_SESSION};



pub(crate) async fn response_app(req: Request<IncomingBody>, token: &str) -> WResult<Response<ResponseBody>> {
    let mut machine_configs = {
        let machines_id = req.headers().get("Machines").cloned().ok_or(WError::Empty)?;
        let machines_id = decrypt_str_to_str(machines_id.to_str().unwrap(), token)?;

        let s = WEB_SESSION.login.read().await;
        let machines = s.as_ref().get(token);
        if machines.is_none() {
            let data = json!({
                "msg": "登录失败",
                "data": "No machine found",
            }).to_string();
            let res = Response::builder()
                .status(StatusCode::UNAUTHORIZED)
                .header(header::CONTENT_TYPE, "application/json")
                .body(response_body(&data, token)).unwrap();
            return Ok(res);
        }
        let machines = machines.unwrap();
        machines_id.split(";").filter_map(| machine_id | {
            machines.get(machine_id).map(| v | (machine_id.to_owned(), v.config.clone()))
        }).collect::<Vec<_>>()
    };

    if machine_configs.is_empty() {
        let data = json!({
            "msg": "请求错误",
            "data": "No machine found",
        }).to_string();
        let res = Response::builder()
            .status(StatusCode::FORBIDDEN)
            .header(header::CONTENT_TYPE, "application/json")
            .body(response_body(&data, token)).unwrap();
        return Ok(res);
    }

    let f = req.headers().get("File").cloned();
    let body_size = req.headers().get(header::CONTENT_LENGTH)
        .ok_or(WError::DataError("No content-length found".to_owned()))?
        .to_str().unwrap()
        .parse::<usize>()?;
    let mut whole_body = req.collect().await.unwrap().aggregate().reader();

    let mut res = HashMap::new();
    let mut err = HashMap::new();

    if let Some(f) = f {
        let args_temp = f.to_str().unwrap().split(" ").map(| v | v.to_string()).collect::<Vec<_>>();
        let app = App::try_parse_from(args_temp)?;
        if let App::FileUpload(mut app) = app {
            let machine_config_last = machine_configs.pop().unwrap();
            let status = if machine_configs.is_empty() {
                // 就一个machine，直接上传
                Some((body_size, Box::new(whole_body) as Box<dyn Read + Send>))
            } else {
                // 需要上传到多个machine，先保存到本地
                let p_temp = get_wtask_data_dir("file", true)?.join(app.data.file.file_name().unwrap());
                let mut f_temp = File::create(&p_temp)?;
                copy(&mut whole_body, &mut f_temp)?;
                app.data.file = p_temp;
                None
            };
            let app_res = cli_app(app.clone(), &machine_config_last.1, status, msg_func_none).await;
            match app_res {
                Ok(Some(v)) => {
                    res.insert(machine_config_last.0, v.into());
                },
                Ok(_) => {
                    err.insert(machine_config_last.0, "app no output".to_owned());
                },
                Err(e) => {
                    err.insert(machine_config_last.0, format!("app run error: {}", e));
                }
            }
            for (machine_id, machine_config) in machine_configs {
                let app_res = cli_app(app.clone(), &machine_config, None, msg_func_none).await;
                match app_res {
                    Ok(Some(v)) => {
                        res.insert(machine_id, v.into());
                    },
                    Ok(_) => {
                        err.insert(machine_id, "app no output".to_owned());
                    },
                    Err(e) => {
                        err.insert(machine_id, format!("app run error: {}", e));
                    }
                }
            }
        } else {
            return Err(WError::ParseError("App error".to_string()));
        }
    } else {
        let body_de = parse_reader(whole_body, token)?;
        let args_temp = serde_from_str::<Vec<String>>(&body_de)?;
        let app = App::try_parse_from(args_temp)?;

        if let App::Shell(_) | App::Port(_) | App::Room(_) = app {
            let data = json!({
                "msg": "请求错误",
                "data": "app not allowed",
            }).to_string();
            let res = Response::builder()
                .status(StatusCode::FORBIDDEN)
                .header(header::CONTENT_TYPE, "application/json")
                .body(response_body(&data, token)).unwrap();
            return Ok(res);
        }
        
        let app_req = app.client_handle_before(msg_func_none)?;
        if app_req.is_none() {
            let data = json!({
                "msg": "请求错误",
                "data": "app no need to run",
            }).to_string();
            let res = Response::builder()
                .status(StatusCode::FORBIDDEN)
                .header(header::CONTENT_TYPE, "application/json")
                .body(response_body(&data, token)).unwrap();
            return Ok(res);
        }
        let app = app_req.unwrap();
        for (machine_id, machine_config) in machine_configs {
            let app_res = cli_appres(app.clone(), &machine_config, msg_func_none).await;
            match app_res {
                Ok(AppRes::Stream(stream_reader)) => {
                    return response_file_stream(stream_reader, false, "application/octet-stream");
                },
                Ok(v) => {
                    res.insert(machine_id, v);
                },
                Err(e) => {
                    err.insert(machine_id, format!("app run error: {}", e));
                }
            }
        }
    }
    
    
    if !err.is_empty() {
        let json = json!({
            "msg": "运行失败",
            "data": err,
        }).to_string();
        let res = Response::builder()
            .status(StatusCode::BAD_REQUEST)
            .header(header::CONTENT_TYPE, "application/json")
            .body(response_body(&json, token)).unwrap();
        return Ok(res);
    }
    let json = serde_to_string(&res)?;
    let response = Response::builder()
        .status(StatusCode::OK)
        .header(header::CONTENT_TYPE, "application/json")
        .body(response_body(&json, token)).unwrap();
    Ok(response)
}